/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "KubeClient.h"
#include "protect_engines/kubernetes/common/KubeErrorCodes.h"
#include "curl_http/HttpStatus.h"
#include "json/json.h"
#include "config_reader/ConfigIniReader.h"

using Module::HttpRequest;
using Module::IHttpResponse;
using Module::IHttpClient;
namespace {
const std::string MODULE_NAME = "KubeClient";
const std::string METHOD_POST = "POST";
const uint32_t SEND_HTTP_DELAY_TIME = 3;  // 3s delay
const uint32_t HTTP_TIME_OUT = 30;
const uint32_t SEND_HTTP_MAX_RETRY_TIMES = 3;
}

namespace KubernetesPlugin {
KubeClient::KubeClient() {}
KubeClient::~KubeClient() {}

int32_t KubeClient::SendRequest(const HttpRequest& httpReq, HttpResponseInfo& httpResponse)
{
    return Send(httpReq, httpResponse);
}

int32_t KubeClient::CheckAccessAuthentication(const AccessAuthParam& accessAuth, const std::string& url,
    HttpResponseInfo& httpResponse)
{
    Json::Value body;
    body["username"] = accessAuth.m_userName;
    body["password"] = accessAuth.m_userkey;
    body["scope"] = accessAuth.m_scope;

    Json::FastWriter writer;
    std::string bodyString = writer.write(body);

    HttpRequest httpReqest;
    InitHttpRequest(httpReqest);
    httpReqest.method = METHOD_POST;
    httpReqest.url = url;
    httpReqest.body = std::move(bodyString);
    httpReqest.isVerify = Module::CACertVerification::DO_NOT_VERIFY;

    return Send(httpReqest, httpResponse);
}

void KubeClient::InitHttpRequest(HttpRequest& req)
{
    req.method = "";
    req.url = "";
    req.certCA = "";
    req.clientCert = "";
    req.clientKey = "";
    req.tmpUrl = "";
    req.vmPathInfo1 = "";
    req.vmPathInfo0 = "";
    req.cert = "";
    req.heads.clear();
    req.body = "";
    req.specialNetworkCard = "";
    req.isVerify = Module::CACertVerification::DO_NOT_VERIFY;
    req.localFilePath = "";
    req.domainInfo = "";
    req.vSphereLoginInfo = "";
}

int32_t KubeClient::Send(const HttpRequest& req, HttpResponseInfo& httpResponse)
{
    int32_t iRet = FAILED;
    IHttpClient* httpClient = IHttpClient::GetInstance();
    if (httpClient == nullptr) {
        INFOLOG("Initialize httpClient failed.");
        return iRet;
    }

    std::shared_ptr<IHttpResponse> dpaHttpRespone = nullptr;
    uint32_t retryTimes = 0;
    while (retryTimes < SEND_HTTP_MAX_RETRY_TIMES) {
        uint32_t connectTimeOut = Module::ConfigReader::getUint("General", "ConnectTimeOut");
        uint32_t totalTimeOut = Module::ConfigReader::getUint("General", "TotalTimeOut");
        dpaHttpRespone = httpClient->SendMemCertRequest(req, connectTimeOut, totalTimeOut);
        if (dpaHttpRespone == nullptr) {
            INFOLOG("DpaHttpRespone is null.%d", WIPE_SENSITIVE(retryTimes));
            retryTimes++;
            continue;
        }
        int32_t errCode = dpaHttpRespone->GetErrCode();
        httpResponse.m_statusCode = dpaHttpRespone->GetHttpStatusCode();
        if (dpaHttpRespone->Success()) {
            httpResponse.m_body = dpaHttpRespone->GetBody();
            iRet = SUCCESS;
            break;
        } else if (!IsNetworkError(httpResponse.m_statusCode)) {
            // 如果因为非网络原因失败，不用再重试
            httpResponse.m_body = dpaHttpRespone->GetBody();
            iRet = SUCCESS;
            break;
        }

        retryTimes++;
        sleep(SEND_HTTP_DELAY_TIME);
    }
    IHttpClient::ReleaseInstance(httpClient);

    return iRet;
}

bool KubeClient::IsNetworkError(const uint32_t statusCode) const
{
    switch (statusCode) {
        case Module::SC_BAD_GATEWAY:
        case Module::SC_SERVICE_UNAVAILABLE:
        case Module::SC_GATEWAY_TIMEOUT:
        case Module::SC_INTERNAL_SERVER_ERROR:
        case 0: {
            return true;
        }
        default:
            return false;
    }
}
}